import os

import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm
import logging
import pandas as pd
from datetime import datetime

import calculate
import liveness_api


logging.basicConfig(level=logging.INFO)




def evaluate_liveness(real_folder, fake_folder, provider, thresholds):

    """
    评估活体检测性能并绘制曲线
    real_folder: 真脸文件夹
    fake_folder：假脸文件夹
    provider: 算法编号
    thresholds: 阈值
    """

    # 创建结果列表
    results = []
    real_scores = []
    fake_scores = []
    
    # 处理真脸图片
    logging.info("处理真脸图片...")
    for img_name in tqdm(os.listdir(real_folder)):
        img_path = os.path.join(real_folder, img_name)
        score = liveness_api.get_liveness_score(img_path, provider)
        if score is not None:
            real_scores.append(score)
            results.append({
                'image_path': img_path,
                'image_name': img_name,
                'type': 'real',
                'score': score,
                'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            })
    
    # 处理假脸图片
    logging.info("处理假脸图片...")
    for img_name in tqdm(os.listdir(fake_folder)):
        img_path = os.path.join(fake_folder, img_name)
        score = liveness_api.get_liveness_score(img_path, provider)
        if score is not None:
            fake_scores.append(score)
            results.append({
                'image_path': img_path,
                'image_name': img_name,
                'type': 'fake',
                'score': score,
                'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            })
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    results_df = pd.DataFrame(results)
    results_df.to_csv(f'全部照片的活体检测结果{timestamp}.csv', index=False, encoding='utf-8-sig')
    
    # 计算不同阈值下的指标
    metrics_list = []
    
    for threshold in thresholds:
        metrics = calculate.calculate_metrics(real_scores, fake_scores, threshold)
        metrics['threshold'] = threshold
        metrics_list.append(metrics)
    
    metrics_df = pd.DataFrame(metrics_list)
    
    # 设置中文字体
    try:
        # Windows系统
        plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']  # 优先使用黑体，其次微软雅黑
        plt.rcParams['axes.unicode_minus'] = False
    except:
        try:
            # Linux系统
            plt.rcParams['font.sans-serif'] = ['WenQuanYi Micro Hei']  # Linux常见中文字体
        except:
            try:
                # macOS系统
                plt.rcParams['font.sans-serif'] = ['PingFang SC', 'Heiti TC']  # macOS常见中文字体
            except:
                logging.warning("无法设置中文字体，图表中的中文可能无法正确显示")
                # 使用默认字体
                plt.rcParams['font.sans-serif'] = ['DejaVu Sans']

    # 1. FRR曲线
    plt.figure(figsize=(10, 6))
    plt.plot(thresholds, metrics_df['far'], 'b-', linewidth=2, marker='o', markersize=4)
    plt.fill_between(thresholds, metrics_df['far'], alpha=0.2)
    plt.xlabel('阈值')
    plt.ylabel('FAR (FP / (FP + TN))')
    plt.title('不同阈值下的假脸被错误接收率(FAR)变化')
    plt.grid(True, linestyle='--', alpha=0.7)
    # 在最佳阈值处添加标注
    best_recall_idx = metrics_df['far'].idxmax()
    best_threshold = metrics_df.loc[best_recall_idx, 'threshold']
    best_recall = metrics_df.loc[best_recall_idx, 'far']
    plt.plot(best_threshold, best_recall, 'ro', markersize=8)
    plt.annotate(f'最佳阈值: {best_threshold:.2f}\n假脸被错误接收率: {best_recall:.2%}',
                 xy=(best_threshold, best_recall), xytext=(10, 10),
                 textcoords='offset points', ha='left', va='bottom',
                 bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
                 arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
    plt.tight_layout()
    plt.savefig(f'假脸被错误接收率(FAR)曲线_{timestamp}.png', dpi=300, bbox_inches='tight')
    plt.close()

    # 1. FRR曲线
    plt.figure(figsize=(10, 6))
    plt.plot(thresholds, metrics_df['frr'], 'b-', linewidth=2, marker='o', markersize=4)
    plt.fill_between(thresholds, metrics_df['frr'], alpha=0.2)
    plt.xlabel('阈值')
    plt.ylabel('FRR (FN / (TP + FN))')
    plt.title('不同阈值下的真脸拒绝率(FRR)变化')
    plt.grid(True, linestyle='--', alpha=0.7)
    # 在最佳阈值处添加标注
    best_recall_idx = metrics_df['frr'].idxmax()
    best_threshold = metrics_df.loc[best_recall_idx, 'threshold']
    best_recall = metrics_df.loc[best_recall_idx, 'frr']
    plt.plot(best_threshold, best_recall, 'ro', markersize=8)
    plt.annotate(f'最佳阈值: {best_threshold:.2f}\n真脸拒绝率: {best_recall:.2%}',
                 xy=(best_threshold, best_recall), xytext=(10, 10),
                 textcoords='offset points', ha='left', va='bottom',
                 bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
                 arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
    plt.tight_layout()
    plt.savefig(f'真脸拒绝率(FRR)曲线_{timestamp}.png', dpi=300, bbox_inches='tight')
    plt.close()
    
    # 1. 绘制Recall曲��
    plt.figure(figsize=(10, 6))
    plt.plot(thresholds, metrics_df['recall'], 'b-', linewidth=2, marker='o', markersize=4)
    plt.fill_between(thresholds, metrics_df['recall'], alpha=0.2)
    plt.xlabel('阈值')
    plt.ylabel('召回率 (TP / (TP + FN))')
    plt.title('不同阈值下的真脸识别率(Recall)变化')
    plt.grid(True, linestyle='--', alpha=0.7)
    # 在最佳阈值处添加标注
    best_recall_idx = metrics_df['recall'].idxmax()
    best_threshold = metrics_df.loc[best_recall_idx, 'threshold']
    best_recall = metrics_df.loc[best_recall_idx, 'recall']
    plt.plot(best_threshold, best_recall, 'ro', markersize=8)
    plt.annotate(f'最佳阈值: {best_threshold:.2f}\n召回率: {best_recall:.2%}',
                xy=(best_threshold, best_recall), xytext=(10, 10),
                textcoords='offset points', ha='left', va='bottom',
                bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
                arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
    plt.tight_layout()
    plt.savefig(f'召回率曲线_{timestamp}.png', dpi=300, bbox_inches='tight')
    plt.close()
    
    # 2. 分别绘制真脸和假脸的检测结果
    # 真脸照片结果
    plt.figure(figsize=(10, 6))
    real_df = results_df[results_df['type'] == 'real'].sort_values('timestamp')
    plt.plot(range(len(real_df)), real_df['score'], 'g-', marker='o', markersize=4)
    # plt.axhline(y=best_threshold, color='r', linestyle='--', label=f'最佳阈值: {best_threshold:.2f}')
    plt.xlabel('照片序号')
    plt.ylabel('活体检测分数')
    plt.title('真脸照片活体检测结果')
    plt.legend()
    plt.grid(True, linestyle='--', alpha=0.7)
    plt.tight_layout()
    plt.savefig(f'真脸照片检测结果_{timestamp}.png', dpi=300, bbox_inches='tight')
    plt.close()
    
    # 假脸照片结果
    plt.figure(figsize=(10, 6))
    fake_df = results_df[results_df['type'] == 'fake'].sort_values('timestamp')
    plt.plot(range(len(fake_df)), fake_df['score'], 'r-', marker='o', markersize=4)
    # plt.axhline(y=best_threshold, color='r', linestyle='--', label=f'最佳阈值: {best_threshold:.2f}')
    plt.xlabel('照片序号')
    plt.ylabel('活体检测分数')
    plt.title('假脸照片活体检测结果')
    plt.legend()
    plt.grid(True, linestyle='--', alpha=0.7)
    plt.tight_layout()
    plt.savefig(f'假脸照片检测结果_{timestamp}.png', dpi=300, bbox_inches='tight')
    plt.close()
    
    # 绘制HTER曲线
    plt.figure(figsize=(10, 6))
    plt.plot(thresholds, metrics_df['hter'], 'r-', linewidth=2, marker='o', markersize=4)
    plt.fill_between(thresholds, metrics_df['hter'], alpha=0.2)
    plt.xlabel('阈值')
    plt.ylabel('HTER ((FAR + FRR) / 2)')
    plt.title('不同阈值下的半总错误率(HTER)变化')
    plt.grid(True, linestyle='--', alpha=0.7)
    # 找到HTER最小值点
    best_hter_idx = metrics_df['hter'].idxmin()
    best_hter_threshold = metrics_df.loc[best_hter_idx, 'threshold']
    best_hter = metrics_df.loc[best_hter_idx, 'hter']
    # 在最佳点添加标注
    plt.plot(best_hter_threshold, best_hter, 'go', markersize=8)
    plt.annotate(f'最佳阈值: {best_hter_threshold:.2f}\nHTER: {best_hter:.2%}',
                xy=(best_hter_threshold, best_hter), xytext=(10, 10),
                textcoords='offset points', ha='left', va='bottom',
                bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
                arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
    plt.tight_layout()
    plt.savefig(f'hter_curve_{timestamp}.png', dpi=300, bbox_inches='tight')
    plt.close()
    
    # 输出最佳阈值下的指标
    best_metrics = metrics_df.iloc[best_recall_idx]
    logging.info("\n最佳阈值下的评估指标:")
    logging.info(f"阈值: {best_threshold:.3f}")
    logging.info(f"TP (真脸被正确识别为真脸): {best_metrics['TP']}")
    logging.info(f"TN (假脸被正确识别为假脸): {best_metrics['TN']}")
    logging.info(f"FP (假脸被错误识别为真脸): {best_metrics['FP']}")
    logging.info(f"FN (真脸被错误识别为假脸): {best_metrics['FN']}")
    logging.info(f"Recall (真脸识别率): {best_metrics['recall']:.3%}")
    logging.info(f"FRR (真脸拒绝率): {best_metrics['frr']:.3%}")
    logging.info(f"FAR (假脸接受率): {best_metrics['far']:.3%}")
    logging.info(f"HTER (半总错误率): {best_metrics['hter']:.3%}")
    logging.info(f"\n最小HTER对应的阈值: {best_hter_threshold:.3f}")
    logging.info(f"最小HTER值: {best_hter:.3%}")
    
    # ROC曲线的绘制
    plt.figure(figsize=(10, 6))
    plt.plot(metrics_df['far'], 1 - metrics_df['frr'], 'b-', linewidth=2, marker='o', markersize=4)
    # 添加对角线
    plt.plot([0, 1], [0, 1], 'r--', linewidth=1, label='随机猜测')
    plt.xlabel('FAR (假接受率)')
    plt.ylabel('1 - FRR (真接受率)')
    plt.title('ROC曲线')
    plt.grid(True, linestyle='--', alpha=0.7)
    # 计算AUC (Area Under Curve)
    auc = np.trapz(1 - metrics_df['frr'], metrics_df['far'])
    # 在图中添加AUC值
    plt.text(0.98, 0.02, f'AUC = {auc:.3f}',
             horizontalalignment='right',
             verticalalignment='bottom',
             bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5))
    # 找到最佳平衡点（距离对角线最远的点）
    distances = np.abs((1 - metrics_df['frr']) - metrics_df['far']) / np.sqrt(2)
    best_idx = distances.idxmax()
    best_point_far = metrics_df.loc[best_idx, 'far']
    best_point_trr = 1 - metrics_df.loc[best_idx, 'frr']
    best_point_threshold = metrics_df.loc[best_idx, 'threshold']
    # 在最佳点添加标注
    plt.plot(best_point_far, best_point_trr, 'go', markersize=8)
    plt.annotate(f'最佳阈值: {best_point_threshold:.2f}\nFAR: {best_point_far:.2%}\nTRR: {best_point_trr:.2%}',
                xy=(best_point_far, best_point_trr), xytext=(10, -10),
                textcoords='offset points', ha='left', va='top',
                bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
                arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
    plt.legend()
    plt.tight_layout()
    plt.savefig(f'roc_curve_{timestamp}.png', dpi=300, bbox_inches='tight')
    plt.close()
    # 在日志中添加ROC相关指标
    logging.info(f"\nROC曲线相关指标:")
    logging.info(f"AUC: {auc:.3f}")
    logging.info(f"ROC最佳平衡点:")
    logging.info(f"  阈值: {best_point_threshold:.3f}")
    logging.info(f"  FAR: {best_point_far:.3%}")
    logging.info(f"  TRR (1-FRR): {best_point_trr:.3%}")

    # 使用最佳阈值记录错误识别的照片
    best_threshold = metrics_df.loc[best_recall_idx, 'threshold']
    # 创建两个错误识别记录的列表
    fn_records = []  # 真脸误判为假脸
    fp_records = []  # 假脸误判为真脸
    # 记录误判的照片
    for result in results:
        if result['type'] == 'real' and result['score'] < best_threshold:
            fn_records.append({
                'image_path': result['image_path'],
                'image_name': result['image_name'],
                'score': result['score'],
                'threshold': best_threshold
            })
        elif result['type'] == 'fake' and result['score'] >= best_threshold:
            fp_records.append({
                'image_path': result['image_path'],
                'image_name': result['image_name'],
                'score': result['score'],
                'threshold': best_threshold
            })
    # 保存错误识别记录到不同文件
    if fn_records:
        fn_df = pd.DataFrame(fn_records)
        fn_df.to_csv(f'最佳阈值下真脸误判为假脸(FN)的照片{timestamp}.csv', index=False, encoding='utf-8-sig')
        logging.info(f"\n最佳阈值下真脸误判为假脸(FN)数量: {len(fn_records)}")
        logging.info("详细记录已保存到 最佳阈值下真脸误判为假脸(FN)的照片_{timestamp}.csv")
        # 输出具体案例
        logging.info("\n真脸误判案例:")
        for record in fn_records:
            logging.info(f"图片: {record['image_name']}, 分数: {record['score']:.3f}")
    if fp_records:
        fp_df = pd.DataFrame(fp_records)
        fp_df.to_csv(f'最佳阈值下假脸误判为真脸(FP)的照片_{timestamp}.csv', index=False, encoding='utf-8-sig')
        logging.info(f"\n最佳阈值下假脸误判为真脸(FP)数量: {len(fp_records)}")
        logging.info("详细记录已保存到 最佳阈值下假脸误判为真脸(FP)的照片_{timestamp}.csv")
        # 输出具体案例
        logging.info("\n假脸误判案例:")
        for record in fp_records:
            logging.info(f"图片: {record['image_name']}, 分数: {record['score']:.3f}")
    
    return metrics_df

if __name__ == "__main__":
    real_folder = "D://Project//livenessDetect//true_eva//"
    fake_folder = "D://Project//livenessDetect//fake_eva//"
    thresholds = np.linspace(0.5, 2, 16)
    evaluate_liveness(real_folder, fake_folder, "2", thresholds)